package com.zvipz.ss.diamonds.bas.gateway.filter;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import com.zvipz.ss.diamonds.bas.gateway.base.R;
import com.zvipz.ss.diamonds.bas.gateway.feignei.AuthInfo;
import com.zvipz.ss.diamonds.bas.gateway.feignei.AuthInterface;

import cn.hutool.core.util.URLUtil;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;


/**
 * 过滤器
 *
 * @author zuihou
 * @date 2019/07/31
 */
// @Component
@Slf4j
@EnableConfigurationProperties({IgnoreTokenProperties.class})
public class TokenContextFilter implements GlobalFilter, Ordered {
    @Value("${spring.profiles.active:dev}")
    protected String profiles;
    @Value("${zuihou.database.multiTenantType:SCHEMA}")
    protected String multiTenantType;

    @Autowired
    private IgnoreTokenProperties ignoreTokenProperties;

    @Autowired
    private AuthInterface authInterface;

    String BEARER_HEADER_KEY = "token";
    String JWT_KEY_ACCOUNT = "account";
    String JWT_KEY_USER_ID = "userid";
    String JWT_KEY_NAME = "name";

    String TEST_TOKEN = "Bearer test";
    String TEST = "test";
    String PROD = "prod";

    @Override
    public int getOrder() {
        return -1000;
    }

    /**
     * 忽略应用级token
     *
     * @return
     */
    protected boolean isIgnoreToken(String path) {
        return ignoreTokenProperties.isIgnoreToken(path);
    }

    protected String getHeader(String headerName, ServerHttpRequest request) {
        HttpHeaders headers = request.getHeaders();
        String token = "";
        if (headers == null || headers.isEmpty()) {
            return token;
        }

        token = headers.getFirst(headerName);

        if (StringUtils.isNotBlank(token)) {
            return token;
        }

        return request.getQueryParams().getFirst(headerName);
    }

    protected boolean isDev(String token) {
        return !PROD.equalsIgnoreCase(profiles) && (TEST_TOKEN.equalsIgnoreCase(token) || TEST.equalsIgnoreCase(token));
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        ServerHttpRequest.Builder mutate = request.mutate();

        AuthInfo authInfo = null;
        try {
            // 忽略 token 认证的接口
            if (isIgnoreToken(request.getPath().toString())) {
                log.debug("access filter not execute");
                return chain.filter(exchange);
            }

            //获取token， 解析，然后想信息放入 heade
            //3, 获取token
            String token = getHeader(BEARER_HEADER_KEY, request);

            // 测试环境 token=test 时，写死一个用户信息，便于测试
            if (isDev(token)) {
                authInfo = new AuthInfo().setUser_name("zuihou").setUser_id(1L).setName("测试用户");
            }

            // 4, 验证 token
            if (authInfo == null) {
                authInfo = authInterface.checkToken(token);
                if (authInfo == null || StringUtils.isNotBlank(authInfo.getError())) {
                    throw new Exception(authInfo.getError_description());
                }
            }
        } catch (Exception e) {
            log.error("Error", e);
            return errorResponse(response, "验证token出错", R.FAIL_CODE, 200);
        }

        //6, 转换，将 token 解析出来的用户身份 和 解码后的tenant、Authorization 重新封装到请求头
        if (authInfo != null) {
            addHeader(mutate, JWT_KEY_ACCOUNT, authInfo.getUser_name());
            addHeader(mutate, JWT_KEY_USER_ID, authInfo.getUser_id());
            addHeader(mutate, JWT_KEY_NAME, authInfo.getName());
        }

        ServerHttpRequest build = mutate.build();
        return chain.filter(exchange.mutate().request(build).build());
    }

    private void addHeader(ServerHttpRequest.Builder mutate, String name, Object value) {
        if (value == null) {
            return;
        }
        String valueStr = value.toString();
        String valueEncode = URLUtil.encode(valueStr);
        mutate.header(name, valueEncode);
    }

    protected Mono<Void> errorResponse(ServerHttpResponse response, String errMsg, int errCode, int httpStatusCode) {
        R<?> tokenError = R.fail(errCode, errMsg);
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        DataBuffer dataBuffer = response.bufferFactory().wrap(tokenError.toString().getBytes());
        return response.writeWith(Mono.just(dataBuffer));
    }

}
